
大多数上下文中，可以使用逻辑操作符组合多个类型特征谓词。模板元编程的上下文中，这并不够:

\begin{itemize}
\item 
必须处理可能失败的特征(不完整的类型)。

\item 
组合类型特征定义。
\end{itemize}

为此提供了类型特征std::conjunction<>，std::disjunction<>和std::negation<>。

这些辅助函数会短路布尔值的计算(分别在\&\&和||的第一个false后中止计算，或第一个true后中止计算)。例如，若使用不完整类型:

\begin{lstlisting}[style=styleCXX]
struct X {
	X(int); // converts from int
};
struct Y; // incomplete type
\end{lstlisting}

因为is\_constructible会导致(不完整类型的)未定义行为(尽管有些编译器接受这段代码)，所以以下代码可能无法编译:

\begin{lstlisting}[style=styleCXX]
// undefined behavior:
static_assert(std::is_constructible<X,int>{}
				|| std::is_constructible<Y,int>{},
			"can’t init X or Y from int");
\end{lstlisting}

因为is\_constructible<X，int>已经产生了true，以下语句保证可以编译:

\begin{lstlisting}[style=styleCXX]
// OK:
static_assert(std::disjunction<std::is_constructible<X, int>,
								std::is_constructible<Y, int>>{},
			"can’t init X or Y from int");
\end{lstlisting}

另一个是通过逻辑组合现有类型特征，来定义新类型特征的一种简单方法，可以定义一个特性来检查一个类型是否“不是指针”(既不是指针，也不是成员指针，也不是空指针):

\begin{lstlisting}[style=styleCXX]
template<typename T>
struct isNoPtrT : std::negation<std::disjunction<std::is_null_pointer<T>,
												std::is_member_pointer<T>,
												std::is_pointer<T>>>
{
};
\end{lstlisting}

因为结合了相应的特征类，所以这里不能使用逻辑操作符。根据这个定义，以下方式可用:

\begin{lstlisting}[style=styleCXX]
std::cout << isNoPtrT<void*>::value << ’\n’; // false
std::cout << isNoPtrT<std::string>::value << ’\n’; // true
auto np = nullptr;
std::cout << isNoPtrT<decltype(np)>::value << ’\n’; // false
\end{lstlisting}

并配以相应的变量模板:

\begin{lstlisting}[style=styleCXX]
template<typename T>
constexpr bool isNoPtr = isNoPtrT<T>::value;
\end{lstlisting}

可以这样写:

\begin{lstlisting}[style=styleCXX]
std::cout << isNoPtr<void*> << ’\n’; // false
std::cout << isNoPtr<int> << ’\n’; // true
\end{lstlisting}

最后一个例子，下面的函数模板只有在所有模板参数既不是类，也不是联合时才启用:

\begin{lstlisting}[style=styleCXX]
template<typename... Ts>
std::enable_if_t<std::conjunction_v<std::negation<std::is_class<Ts>>...,
									std::negation<std::is_union<Ts>>...
									>>
print(Ts...)
{
	...
}
\end{lstlisting}

省略号放在std::negation后面，以便用于参数包的每个元素。

\begin{table}[H]
	\begin{center}
	\begin{tabular}{l|l}
		\hline
		\textbf{特性}                            & \textbf{作用}                                   \\ \hline
		conjunction\textless{}B... \textgreater{} & 逻辑\textbf{和}布尔特征B…(C++17) \\ \hline
		disjunction\textless{}B... \textgreater{} & 逻辑\textbf{或}布尔特征B…(C++17)  \\ \hline
		negation\textless{}B \textgreater{}       & 逻辑\textbf{非}布尔特征B(C++17)     \\ \hline
	\end{tabular}
	\end{center}
\end{table}

\begin{center}
表D.8. 组合其他类型特征
\end{center}

\textbf{std::conjunction<B...>::value}

\textbf{std::disjunction<B...>::value}

\begin{itemize}
\item 
传入的特性B...至少有一个或全部为布尔特征时，生成true

\item 
逻辑上分别对传入的特征使用操作符\&\&或||。

\item 
这两个特性都会短路(第一个false或true之后中止计算)。

\item 
C++17后可用
\end{itemize}

\textbf{std::negation<B>::value}

\begin{itemize}
\item 
传入的B是布尔特征则生成false。

\item 
对传入的特性使用逻辑非运算符。

\item 
C++17后可用
\end{itemize}












